/* 
*  Copyright (C) 2009-2010 WCG Project
*
*  This program is free software; you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by the
*  Free Software Foundation; either version 2 of the License, or (at
*  your option) any later version.
*
*  This program is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software Foundation,
*  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*  In addition, as a special exception, the author gives permission to
*  link the code of this program with the Half-Life Game Engine ("HL
*  Engine") and Modified Game Libraries ("MODs") developed by Valve,
*  L.L.C ("Valve"). You must obey the GNU General Public License in all
*  respects for all of the code used other than the HL Engine and MODs
*  from Valve. If you modify this file, you may extend this exception
*  to your version of the file, but you are not obligated to do so. If
*  you do not wish to do so, delete this exception statement from your
*  version.
*/



/*
*	XP Functions
*/

// Objective Modifiers
#define DEFUSING_BOMB		5		// XP awarded when the user starts to defuse the bomb
#define DEFUSED_BOMB		15		// XP awarded when the user defuses the bomb
#define PLANTING_BOMB		5		// XP awarded when the user starts planting the bomb
#define PLANT_BOMB			10		// XP awarded when the user plants the bomb
#define SPAWN_BOMB			10		// XP awarded when the user spawns with the bomb
#define BOMB_PICKUP			10		// XP awarded when the user picks up the bomb
#define TOUCH_HOSTAGE		5		// XP awarded when the user touches a hostage
#define RESCUE_HOSTAGE		15		// XP awarded when the user rescues the hostage
#define KILL_HOSTAGE		20		// XP lost when killing a hostage
#define SPAWN_VIP			10		// XP awarded for spawning as the VIP
#define ESCAPE_VIP			15		// XP awarded for escaping as the VIP
#define OBJ_RADIUS			500		// Nearby radius to award XP for helping complete objectives

// Kill modifiers
#define KILL_HEADSHOT		5		// XP awarded for getting a headshot
#define KILL_HOSTAGE_SAVER	5		// XP awarded for killing the hostage saver
#define	KILL_DEFUSER		5		// XP awarded for killing the defuser
#define KILL_PLANTER		5		// XP awarded for killing the planter
#define KILL_BOMB_CARRIER	5		// XP awarded for killing the bomb carrier
#define KILL_VIP			5		// XP awarded for killing the VIP
#define KILL_RADIUS			250		// Nearby radius to award XP

#define WIN_ROUND			25		// XP awarded for winning the round

//#define XP_LOG_TABLES				// If defined,  xp tables will be dumped to log_amx (good for reviewing values)


#define XP_NEARBY_RADIUS		300

// Holds the XP Multipliers per weapon
new Float:fWpnXPMultiplier[CSW_WAR3_MAX] = {1.0}


// START FROM CSX.INC - This is a hack b/c we can't include CSX and DODX in the same plugin :/
forward bomb_planting(planter)
forward bomb_planted(planter)
forward bomb_defusing(defuser)
forward bomb_defused(defuser)
// END FROM CSX.INC

//---------------------------------------
// Called when a user begins defusing the bomb
//---------------------------------------
public bomb_defusing( defuser )
{
	// Make sure that this isn't called more than once per round
	if( !PlayerInfo[defuser][PI_HasBegunPlantingOrDefusing] )
	{
		new iBonusXP = XP_Give( defuser, DEFUSING_BOMB )

		if ( iBonusXP != 0 )
			client_print( defuser, print_chat, "%s You have been awarded %d XP for starting to defuse the bomb", g_MODclient, iBonusXP )
	}

	PlayerInfo[defuser][PI_HasBegunPlantingOrDefusing] = true

	if( PlayerInfo[defuser][PI_Stealthed] )
		_RemoveStealth( defuser )
}

//---------------------------------------
// Called when a user defuses the bomb
//---------------------------------------
public bomb_defused( defuser )
{
	new iBonusXP = XP_Give( defuser, DEFUSED_BOMB )

	if ( iBonusXP != 0 )
		client_print( defuser, print_chat, "%s You have been awarded %d XP for defusing the bomb", g_MODclient, iBonusXP )

	PlayerInfo[defuser][PI_PlayerRole] = 0

	// The "bartime" event isn't called once the bomb is defused, so we need to reset this manually
	PlayerInfo[defuser][PI_IsPlantingDefusing] = false

	GameInfo[GI_BombPlanted] = false
}

//---------------------------------------
// Called when a user begins planting the bomb
//---------------------------------------
public bomb_planting( planter )
{
	// Make sure that this isn't called more than once per round
	if ( !PlayerInfo[planter][PI_HasBegunPlantingOrDefusing] )
	{
		new iBonusXP = XP_Give( planter, PLANTING_BOMB )

		if ( iBonusXP != 0 )
			client_print( planter, print_chat, "%s You have been awarded %d XP for starting to plant the bomb", g_MODclient, iBonusXP )
	}

	PlayerInfo[planter][PI_PlayerRole] = PLR_BOMB_PLANTER
	PlayerInfo[planter][PI_HasBegunPlantingOrDefusing] = true
}

//---------------------------------------
// Called when a user has planted the bomb
//---------------------------------------
public bomb_planted( planter )
{
	new iBonusXP = XP_Give( planter, PLANT_BOMB )

	if ( iBonusXP != 0 )
		client_print( planter, print_chat, "%s You have been awarded %d XP for planting the bomb", g_MODclient, iBonusXP )

	PlayerInfo[planter][PI_PlayerRole] = 0

	// The "bartime" event isn't called once the bomb is planted, so we need to reset this manually
	PlayerInfo[planter][PI_IsPlantingDefusing] = false	


	// Set and stor bomb information
	GameInfo[GI_BombPlanted] = true

	new iEnt = -1
	static Float:fOrigin[3]

	do
	{
		iEnt = fm_find_ent_by_class( iEnt, "grenade" )
		if( pev_valid( iEnt ) )
		{
			pev( iEnt, pev_model, szTmpMsg, 31 )
			if( equali( szTmpMsg, "models/w_c4.mdl" ) )
			{
				pev( iEnt, pev_origin, fOrigin )
				GameInfo[GI_BombOrigin+(S_GAMEINFO:0)] = _:fOrigin[0]
				GameInfo[GI_BombOrigin+(S_GAMEINFO:1)] = _:fOrigin[1]
				GameInfo[GI_BombOrigin+(S_GAMEINFO:2)] = _:fOrigin[2]
				break
			}
		}
	} while( iEnt )
}


//---------------------------------------
// XP_onDeath
//
//---------------------------------------
XP_onDeath( iVictim, iAttacker, iWeaponIndex )
{
	static /*Float:fLevelFraction, iLevel,*/ iBonusXP, iXP

//	client_print( 0, print_chat, "XP_onDeath. iAttacker=%d, iVictim=%d", iAttacker, iVictim )
//	server_print( "XP_onDeath. iAttacker=%d, iVictim=%d, iWeapon=%d", iAttacker, iVictim, iWeaponIndex )

	// We don't want to give XP to suiciders
	if( iAttacker == iVictim )
		return

	if( !is_user_connected(iAttacker) )
		return
	
//	iLevel = PlayerInfo[iVictim][PI_Level]

//	fLevelFraction = 1.0 + ( ( 1.0 / MAX_LEVELS * (  PlayerInfo[iVictim][PI_Level] - PlayerInfo[iAttacker][PI_Level] ) ) / 2 )
//	iXP =  floatround( (XP_GivenByLevel( iLevel ) * fWpnXPMultiplier[iWeaponIndex]) / fLevelFraction )

	iXP =  floatround( XP_GivenByLevel( PlayerInfo[iAttacker][PI_Level] ) * fWpnXPMultiplier[iWeaponIndex] )


//	client_print( 0, print_chat, "iLevelFraction = %f, iXP=%d", iLevelFraction,  iXP )
//	client_print( 0, print_chat, "xpgiven_lev[iLevel]=%d, iLevel=%d, iXP=%d, iWeaponIndex=%d", xpgiven_lev[iLevel], iLevel, iXP, iWeaponIndex )

	// Check for a team kill
	if( PlayerInfo[iAttacker][PI_TeamID] == PlayerInfo[iVictim][PI_TeamID] )
	{
		// Remove XP since he killed his teammate
		iBonusXP = XP_Give( iAttacker, -1 * iXP )

		// This message should be displayed no matter what XP_kill_objectives is, b/c it's a no-no
		if ( iBonusXP != 0 )
			client_print( iAttacker, print_chat, "%s You have lost %d XP for killing a teammate", g_MODclient, -1 * iBonusXP )

		return
	}

	// Otherwise the player killed the other team

	// Award XP for a kill
	iBonusXP = XP_Give( iAttacker, iXP )

	if ( iBonusXP != 0 )
		client_print( iAttacker, print_chat, "%s You have been awarded %d XP for killing the enemy", g_MODclient, iBonusXP )

	// User had a headshot?  Give bonus XP!
	if( PlayerInfo[iVictim][PI_Hitgroup] == HIT_HEAD )
	{
		iBonusXP = XP_Give( iAttacker, KILL_HEADSHOT )
		
		if ( iBonusXP != 0 )
			client_print( iAttacker, print_chat, "%s You have been awarded %d XP for getting a headshot", g_MODclient, iBonusXP )
	}

	// User killed a hostage rescuer
	if ( PlayerInfo[iVictim][PI_PlayerRole] == PLR_HOSTAGE_RESCUER )
	{
		iBonusXP = XP_Give( iAttacker, KILL_HOSTAGE_SAVER )
		
		if ( iBonusXP != 0 )
			client_print( iAttacker, print_chat, "%s You have been awarded %d XP for killing the hostage rescuer", g_MODclient, iBonusXP )
	}
	// User killed the bomb defuser
	else if ( PlayerInfo[iVictim][PI_PlayerRole] == PLR_BOMB_DEFUSER )
	{
		iBonusXP = XP_Give( iAttacker, KILL_DEFUSER )
		
		if ( iBonusXP != 0 )
			client_print( iAttacker, print_chat, "%s You have been awarded %d XP for killing the bomb defuser", g_MODclient, iBonusXP )
	}
	// User killed the bomb planter
	else if ( PlayerInfo[iVictim][PI_PlayerRole] == PLR_BOMB_PLANTER )
	{
		iBonusXP = XP_Give( iAttacker, KILL_PLANTER )
		
		if ( iBonusXP != 0 )
			client_print( iAttacker, print_chat, "%s You have been awarded %d XP for killing the bomb planter", g_MODclient, iBonusXP )
	}
	// User killed the bomb carrier
	else if ( PlayerInfo[iVictim][PI_PlayerRole] == PLR_BOMB_CARRIER )
	{
		iBonusXP = XP_Give( iAttacker, KILL_BOMB_CARRIER )
		
		if ( iBonusXP != 0 )
			client_print( iAttacker, print_chat, "%s You have been awarded %d XP for killing the bomb carrier", g_MODclient, iBonusXP )
	}
	// user killed the VIP
	else if ( PlayerInfo[iVictim][PI_PlayerRole] == PLR_VIP )
	{
		iBonusXP = XP_Give( iAttacker, KILL_VIP )
		
		if ( iBonusXP != 0 )
			client_print( iAttacker, print_chat, "%s You have been awarded %d XP for killing the VIP", g_MODclient, iBonusXP )
	}

	// Player died, so lets reset their data
	PlayerInfo[iVictim][PI_PlayerRole] = 0
}


//---------------------------------------
// XP_WinRound
//
//---------------------------------------
XP_WinRound( iWinner )
{
	new id, iBonusXP

	// Distribute healing xp
	for( id = 1 ; id <= GameInfo[GI_MaxPlayers] ; id++ )
	{
		if( !is_user_connected(id) || !IsOnTeam(id) )
			continue

		// Is the user on the correct team?
		if( PlayerInfo[id][PI_TeamID] == iWinner )
		{
			iBonusXP = XP_Give( id, WIN_ROUND )
			client_print( id, print_chat, "%s %L", g_MODclient, id, "AWARD_FOR_WINNING_ROUND", iBonusXP )
		}
	}
}


//---------------------------------------
// XP_RoundEnd
//
//---------------------------------------
XP_RoundEnd()
{
	new id, j, iBonusXP, xp, Float:fMultiplier

	// Distribute damage xp
	for( id = 1 ; id <= GameInfo[GI_MaxPlayers] ; id++ )
	{
		if( !is_user_connected(id) || !IsOnTeam(id) )
			continue

		iBonusXP = 0

		for( j = 1 ; j <= GameInfo[GI_MaxPlayers] ; j++ )
		{
			if( g_iDamageDealt[id][j] > 0 )
			{
				if( id == j )
				{
					g_iDamageDealt[id][j] = 0
					continue
				}

				fMultiplier = float( g_iDamageDealt[id][j] ) / 250.0
				xp = XP_GivenByLevel( PlayerInfo[id][PI_Level] )
				iBonusXP += XP_Give( id, ( floatround( float( xp ) * fMultiplier, floatround_ceil ) ) )
				g_iDamageDealt[id][j] = 0
			}
		}


		if( iBonusXP != 0 )
			client_print( id, print_chat, "%s You have been awarded %d XP for inflicting damage!", g_MODclient, iBonusXP )
	}


	// Distribute healing xp
	for( id = 1 ; id <= GameInfo[GI_MaxPlayers] ; id++ )
	{
		if( !is_user_connected(id) || !IsOnTeam(id) )
			continue

		iBonusXP = 0

		for( j = 1 ; j <= GameInfo[GI_MaxPlayers] ; j++ )
		{
			if( g_iHealingDone[id][j] > 0 )
			{
				if( id == j )
				{
					g_iHealingDone[id][j] = 0
					continue
				}

				fMultiplier = float( g_iHealingDone[id][j] ) / 100.0
				xp = XP_GivenByLevel( PlayerInfo[id][PI_Level] )
				iBonusXP += XP_Give( id, ( floatround( float( xp ) * fMultiplier ) ) )
				g_iHealingDone[id][j] = 0
			}
		}

		if( iBonusXP != 0 )
			client_print( id, print_chat, "%s You have been awarded %d XP for healing your teammates!", g_MODclient, iBonusXP )
	}
}


//---------------------------------------
//---------------------------------------
XP_GetByLevel( iLevel )
{
	if ( iLevel < 0 || iLevel > MAX_LEVELS )
		return 0

	return xplevel_lev[iLevel]
}


//---------------------------------------
//---------------------------------------
XP_GivenByLevel( iLevel )
{
	if ( iLevel < 0 || iLevel > MAX_LEVELS )
		return 0

	return xpgiven_lev[iLevel]
}

//---------------------------------------
//---------------------------------------
bool:XP_MinPlayers()
{
	if( g_NumTeamPlayers[TEAM_T] < 1 || g_NumTeamPlayers[TEAM_CT] < 1 )
		return false

	return true
}

//---------------------------------------
// Function will verify if the user's level/information is correct
// and checks to see if the user has gained a level
//---------------------------------------
XP_Check( id, bShowGained = true )
{
	static iSkillID, iSkillLevel, iSkillLevelReq, iSkillMaxLevel, bool:bResetSkills, skillpoints

	new iOldLevel = PlayerInfo[id][PI_Level], i
	
	// Make sure the user doesn't have negative experience
	if ( PlayerInfo[id][PI_XP] < 0 )
		PlayerInfo[id][PI_XP] = 0

	// Determine what the user's level should be
	for( i = 0; i <= MAX_LEVELS; i++ )
	{
		// User has enough XP to advance to the next level
		if( PlayerInfo[id][PI_XP] >= XP_GetByLevel( i ) )
			PlayerInfo[id][PI_Level] = i

		// On this increment the user doesn't have enough XP to advance to the next level
		else
			break
	}



	// User gained a level
	if ( PlayerInfo[id][PI_Level] > iOldLevel && PlayerInfo[id][PI_Class] != CLASS_NONE && bShowGained )
	{
		WCG_StatusText( id, TXT_TOP_CENTER, HUD_AUTO, "%L", id, "YOU_GAINED_A_LEVEL" )
		
		// Only emit the sound if the user is alived!!
		if( !is_user_alive(id) )
			client_cmd( id, "spk %s", g_szSounds[SOUND_LEVELUP] )
		else
			emit_sound( id, CHAN_STATIC, g_szSounds[SOUND_LEVELUP], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )
	}


	
	bResetSkills = false

	skillpoints = PlayerInfo[id][PI_Level] - SM_TotalSkillPointsUsed( id )

	// Negative amount of spent skillpoints isn't good
	if( skillpoints < 0 )
	{
		client_print( id, print_chat, "%s Negative number of skillpoints left.", g_MODclient )
		bResetSkills = true
	}
	else
	{
		for( i = 0 ; i < g_NumAvailableSkills[id] ; i++ )
		{
			iSkillID = g_AvailableSkills[id][i]
	
			if( g_PlayerSkillLevel[id][iSkillID] == 0 )
				continue
	
			iSkillMaxLevel = g_SkillMaxLevel[iSkillID]
			iSkillLevel = g_PlayerSkillLevel[id][iSkillID]
			iSkillLevelReq = g_SkillLevelReq[iSkillID] + iSkillLevel - 1
	
			// Oups, we have a skill at higher level then possible
			if( iSkillLevel > iSkillMaxLevel )
			{
				client_print( id, print_chat, "%s A Selected skills level is higher then possible.", g_MODclient )
				bResetSkills = true
				break
			}
			else if( PlayerInfo[id][PI_Level] < iSkillLevelReq )
			{
				client_print( id, print_chat, "%s Your level is lower then a selected skills minimum requirements.", g_MODclient )
				bResetSkills = true
				break
			}
		}
	}

	if ( bResetSkills )
	{
		client_print( id, print_chat, "%s Invalid skill setup detected. Forcing a skillsreset.", g_MODclient )
		set_task( 1.0, "_XP_ForcedSkillsReset", TASK_DELAYEDFUNCTION + id )
		return
	}

	if( !ValidateAttributes( id ) )
	{
		client_print( id, print_chat, "%s Invalid attributes detected. Forcing an attributesreset.", g_MODclient )
		set_task( 1.0, "_XP_ForcedAttributeReset", TASK_DELAYEDFUNCTION + id )
		return
	}


	// OK lets check the total skills the user has, and maybe show them the selectskills menu
	if( SM_SkillsAvailable( id ) )
		MENU_SelectSkill( id )
	else if( GetAvailableAttributePoints( id ) )
		MENU_SelectAttribute( id )


	WC3_ShowBar( id )
}


public _XP_ForcedSkillsReset( id )
{
	id = GetTaskID( id, TASK_DELAYEDFUNCTION )

	PlayerInfo[id][PI_ResetSkills] = true
	
	WC3_ResetSkills( id )
	PLAYER_SetDefaults( id )
	
	if( SM_SkillsAvailable( id ) )
		MENU_SelectSkill( id )
	else if( GetAvailableAttributePoints( id ) )
		MENU_SelectAttribute( id )

	WC3_ShowBar( id )
}



public _XP_ForcedAttributeReset( id )
{
	id = GetTaskID( id, TASK_DELAYEDFUNCTION )

	PlayerInfo[id][PI_ResetAttributes] = true
	
	WC3_ResetAttributes( id )
	PLAYER_SetDefaults( id )
	
	if( GetAvailableAttributePoints( id ) )
		MENU_SelectAttribute( id )
	else if( SM_SkillsAvailable( id ) )
		MENU_SelectSkill( id )

	WC3_ShowBar( id )
}



//---------------------------------------
//---------------------------------------
XP_Configure()
{
	// Configure based on weapon multiplier

	fWpnXPMultiplier[CSW_USP]				= 1.5
	fWpnXPMultiplier[CSW_DEAGLE]			= 1.15
	fWpnXPMultiplier[CSW_GLOCK18]			= 1.5
	fWpnXPMultiplier[CSW_ELITE]				= 2.0
	fWpnXPMultiplier[CSW_P228]				= 1.5
	fWpnXPMultiplier[CSW_FIVESEVEN]			= 1.5

	fWpnXPMultiplier[CSW_XM1014]			= 1.25
	fWpnXPMultiplier[CSW_M3]				= 1.5

	fWpnXPMultiplier[CSW_MP5NAVY]			= 1.0
	fWpnXPMultiplier[CSW_UMP45]				= 1.25
	fWpnXPMultiplier[CSW_P90]				= 1.25
	fWpnXPMultiplier[CSW_TMP]				= 1.5
	fWpnXPMultiplier[CSW_MAC10]				= 1.5
	fWpnXPMultiplier[CSW_GALIL]				= 1.15
	fWpnXPMultiplier[CSW_FAMAS]				= 1.15

	fWpnXPMultiplier[CSW_AWP]				= 1.0
	fWpnXPMultiplier[CSW_M4A1]				= 1.0
	fWpnXPMultiplier[CSW_AK47]				= 1.0
	fWpnXPMultiplier[CSW_AUG]				= 1.0
	fWpnXPMultiplier[CSW_SG552]				= 1.0
	fWpnXPMultiplier[CSW_G3SG1]				= 1.0
	fWpnXPMultiplier[CSW_SG550]				= 1.0
	fWpnXPMultiplier[CSW_M249]				= 1.25
	fWpnXPMultiplier[CSW_SCOUT]				= 1.5

	fWpnXPMultiplier[CSW_HEGRENADE]			= 1.25
	fWpnXPMultiplier[CSW_KNIFE]				= 3.0

	fWpnXPMultiplier[CSW_C4]				= 0.0
	fWpnXPMultiplier[CSW_SMOKEGRENADE]		= 1.0
	fWpnXPMultiplier[CSW_FLASHBANG]			= 1.0

	fWpnXPMultiplier[CSW_WORLDSPAWN]		= 0.0
	fWpnXPMultiplier[CSW_LIGHTNING]			= 1.0
	fWpnXPMultiplier[CSW_IMMOLATE]			= 1.0
	fWpnXPMultiplier[CSW_SERPENTWARD]		= 1.0
	fWpnXPMultiplier[CSW_SMITE]				= 1.0	
	fWpnXPMultiplier[CSW_THORNS]			= 1.0
	fWpnXPMultiplier[CSW_BLASTWAVE]			= 1.0
	fWpnXPMultiplier[CSW_SERPENTSTING]		= 1.0
	fWpnXPMultiplier[CSW_ARCANESHOT]		= 1.0
	fWpnXPMultiplier[CSW_BANISH]			= 1.0
	fWpnXPMultiplier[CSW_ENTANGLE]			= 1.0
	fWpnXPMultiplier[CSW_CRITICAL_GRENADE]	= 0.75
	fWpnXPMultiplier[CSW_FROSTNOVA_GRENADE]	= 1.25

	XP_BuildXPTables()
}


//---------------------------------------
//---------------------------------------
public XP_BuildXPTables()
{
	XP_BuildLongtermXPTable()

#if defined XP_LOG_TABLES
	new diff = 0
	for( new j=0; j<=MAX_LEVELS; j++ )
	{
		if( j > 0 ) diff = xplevel_lev[j] - xplevel_lev[j-1]
		log_amx( "Level %d   %d xp needed  %d diff   %d xpgiven   %d mod", j, xplevel_lev[j], diff, xpgiven_lev[j], diff / xpgiven_lev[j] )
	}
	
/*	XP_PrintGivenXPTable( 1.0 )
	XP_PrintGivenXPTable( 1.25 )
	XP_PrintGivenXPTable( 1.5 )
	XP_PrintGivenXPTable( 1.75 )
	XP_PrintGivenXPTable( 2.0 )
	XP_PrintGivenXPTable( 2.5 )
	XP_PrintGivenXPTable( 3.0 )
	XP_PrintGivenXPTable( 6.0 )
*/
#endif

	return PLUGIN_CONTINUE
} 

#if defined XP_LOG_TABLES
	XP_PrintGivenXPTable( Float:fMultiplier )
	{
		log_amx( "fMultiplier = %f", fMultiplier )
		for( new j=0; j<=MAX_LEVELS; j++ )
			log_amx( "Level %d  -  %d", j, floatround( XP_GivenByLevel( j ) * fMultiplier ) )
			
		log_amx( " " )
	}
#endif

//---------------------------------------
//---------------------------------------
public XP_BuildLongtermXPTable()
{
	new Float:xpPerLevel = 100.0
	new Float:x
	
	xplevel_lev[0] = 0
	xpgiven_lev[0] = 4

	for( new j=1 ; j <= MAX_LEVELS ; j++ )
	{
		// Sets the amount of xp given for each level
//		xpgiven_lev[j] = XP_BASE + (j * XP_ADD_LEVEL) / XP_GIVEN_DIVIDER

		xpgiven_lev[j] = 4 + floatround( j * 0.7 )

		// Sets the amount of xp needed for next level
		xplevel_lev[j] = xplevel_lev[j-1] + floatround( xpPerLevel )

		xpPerLevel += 160 + xpgiven_lev[j]

		x = float(j) / MAX_LEVELS / 7

		if (j >= 2)
			xplevel_lev[j] = floatround( xplevel_lev[j] * (x + 0.90) )
	}
	return PLUGIN_CONTINUE
} 



//---------------------------------------
// Reset the user's XP to 0
//---------------------------------------
public XP_Reset(id)
{
	PlayerInfo[id][PI_Level] = 0
	PlayerInfo[id][PI_XP] = 0
	SM_ResetSkillLevels( id )
	ResetAttributeLevels( id, 5 )

	// Save after resetting the user's XP
	DB_SaveXP( id, true )

	WC3_ShowBar( id )

	client_print( id, print_chat, "%s %L", g_MODclient, id, "YOUR_XP_HAS_BEEN_RESET" )

	// Reset the skills...
	PLAYER_UpdateInvisibility( id )

	// Undead's Unholy Aura
	PLAYER_UpdateGravity( id )

	// Set the user's speed
	PLAYER_UpdateSpeed( id )

	return PLUGIN_CONTINUE
}	

//---------------------------------------
// Function from war3x thanks ryan!!!
//---------------------------------------
public XP_GetAdminFlag()
{
    new szFlags[24]
    get_pcvar_string( CVAR_admin_flag, szFlags, 23 )

    return ( read_flags( szFlags ) )
}

//---------------------------------------
// XP_Give
//
//---------------------------------------
stock XP_Give( id, iBonusXP )
{
	if( !IsValidUser(id) )
		return 0

	// Make sure we have the minimum amount of players
	if( !XP_MinPlayers() )
		return 0

	// Bonus calculated by:
	// Bonus XP * (lvl of player/10 + 1.0)
	// I.E. if Player is level 10, then it will be Bonus XP * 2.0
	
	if( iBonusXP != 0 )
	{
		new Float:fCurrentLevel = float( PlayerInfo[id][PI_Level] )
		new Float:iLevelMultiplier = ( fCurrentLevel / MAX_LEVELS ) + 1.0
		new iRealBonusXP = floatround(iLevelMultiplier * iBonusXP)
		
		PlayerInfo[id][PI_XP] += iRealBonusXP

		XP_Check( id )

		return iRealBonusXP
	}
	return 0
}

